xeenfandomcom-20200213-history
Sound/Music Drivers
Xeen supports a variety of audio drivers to output wave sound and MIDI music. Each of these drivers is stored in the game's primary resource CC file. These sound drivers are also responsible for providing timing functions to the game engine. Driver Filenames Each driver file is named according to a standard: the filename begins with a prefix which indicates the kind of sound card the driver is for, and the filename ends with the letters "MUS" for a music (MIDI) driver or "SND" for a wave sound driver. Internally, the game uses the terms "speak", "sound", and "music" somewhat interchangeably. "Sound" and "music" generally refer to the MIDI music driver, while "speak" commands refer to the wave sound driver. These driver files consist entirely of standard x86 executable code, and work by exposing "jump" commands at specified locations. The drivers are loaded in to memory when the game starts, and the game jumps to a specific offset in the driver to perform any given function. For example, to initialize the music driver, the game will jump to seg:0000 where "seg" is the segment the driver has been loaded to. The entry points listed below exist in all drivers. The initialization routines use the following variables, all of which (except seg_music) are read directly from the configuration file: * soundAddr is the address of the sound card (default is 0x220) * musicAddr is the address of the music card (default is 0x388) * irq is the sound card's IRQ (default is 7) * volumeSet is a boolean indicating whether or not the game may set the sound card's default volume levels (only used by some drivers) * seg_music is the segment address that the driver has been loaded in to * soundDMA is the DMA setting of the sound card (default is 1) Music Driver Functions "Initialize" Function This function takes four uint16 arguments: musicAddr, volumeSet, initString, soundAddr. initString is a pointer to a theoretical string of MIDI commands that will be passed to the MPU on startup. In practice, it is always 0, and should be safely ignored. "Terminate" Function Shuts down the driver. "Song" Function The "song" function is called with a uint16 command argument, and any number of further arguments depending on the command being executed. Stop Music Command: 0000h Argument: none Stops the music. Called when the music flag is flipped off in the control panel. Restart Command: 0001h Argument: none Restarts the currently playing music to the beginning of the file SetVolume Command: 0100h Argument: Volume Level (uint16) This function is called with two volume levels: 48 and 95, which indicate the probable range of accepted values is 0 to 100. This function is called in the in-game cutscenes, specifically the Sphinx, Golem, Reaper, and Dwarf events. Set "Something" (Song playing?) Command: >0001h but <=00FFh (00CF used everywhere, apparently) Argument: none Sets the "Something" returned by command FFE0h to the command value. Return "Something" (Song playing?) Command: FFE0h Argument: none Returns a byte variable of as yet unknown use. Suspected to be set true when a sound is playing. Can be set by the above Set command. Play Song Command: >0100h Argument: ignored (uint16), stop (uint16) The command value is actually the segment the song has been loaded to in memory. It abuses the segmented nature of real mode by starting the music right at the start of a segment, so the offset 0 is the start of the file. Takes two arguments. The first is ignored, the second is a boolean value which tells the music player that it needs to stop the current note on a channel before starting the next one. Both arguments are always set to 0 anyway. "FX" Function Unexamined. "Interrupt" Function This is an external link to the music interrupt function which is triggered 72.8 times per second. There should never be a need to call this directly. "nop" Two zero bytes that do nothing. "Set Instrument" Function An external link to a "Set Instrument" function similar to the one called in the course of music playback. Two uint16 arguments are channel and instrument number. This is not called anywhere in any known code, and may be a holdover from some debugging functions. Playing Music and Sound Effects Execution In order to play music, the "Song" function must be called with the segment the .M file has been loaded into. The function will set the offset to 0, set the "musicPlaying" boolean to true, and count down timer to 0. Similar in the call to "FX" function, setting the "fxPlaying" flag to true, and the respective count down timer to 0. At the start of every interrupt, first check boolean variable "musicPlaying". If true, there's currently a .M file loaded, and the interrupt will attempt to process it. Assuming music is playing, it then decrements and checks the music delay timer. If it is zero, it proceeds to read the next command from the file and execute it. It will then loop back to the top, reading and executing commands until it encounters command 0x1, which sets the delay timer, and exits the interrupt. If the "musicPlaying" flag is false, or the music delay timer is not yet zero, it proceeds to the FX execution code. In the FX execution code, it checks "fxPlaying" flag. If false, it simply exits the interrupt as there is no music and no FX commands to execute. If the "fxPlaying" flag is true, it decrements and checks the FX delay timer. Similar to the music delay timer, if it is zero, it proceeds to read and execute commands, starting from where it last left off. If the FX delay timer is not yet zero, it exits the interrupt. Command list These are commands used to control the music and sound effects (not to be confused with the digital sound voices) Music (.M) file Commands Commands from the .M file are played sequentially unless otherwise stated. FX Commands FX Commands are stored in internal structures as a sequence of bytes, similar (but different) to the .M music files, though obviously much shorter. This means there are a finite number of effects that can be played, all hard-coded directly in the driver. As a result, each driver also has a slightly different set of commands, though mostly similar. More investigation may be needed for this, though it should be sufficient as a black box now. Sound Driver Functions "Initialize" Function This function takes four uint16 arguments: soundAddr, seg_music, irq, soundDMA. "Speak" Function Unexamined. "Terminate" Function Shuts down the driver. Category:Engine